From 0195181e6d68aed2400fc3b1fcf08295a56ec4bf Mon Sep 17 00:00:00 2001 From: oliskoli Date: Sat, 14 Jul 2007 21:05:23 +0000 Subject: [PATCH] garmin_gpi: Improve support for Garmin address fields. --- garmin_gpi.c | 361 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 232 insertions(+), 129 deletions(-) diff --git a/garmin_gpi.c b/garmin_gpi.c index cdd6d4aae..cd45723ed 100644 --- a/garmin_gpi.c +++ b/garmin_gpi.c @@ -30,6 +30,8 @@ * 2007/05/23: add optional user bitmap * 2007/06/02: new method to compute center (mean) of bounds avoid endless loop in group splitting + * 2007/07/10: put address fields (i.e. city) into GMSD + * 2007/07/12: add write support for new address fields ToDo: @@ -41,6 +43,7 @@ #include "defs.h" #include "cet_util.h" #include "jeeps/gpsmath.h" +#include "garmin_fs.h" #include "garmin_gpi.h" #include #include @@ -55,6 +58,13 @@ #define DEFAULT_ICON "Waypoint" #define WAYPOINTS_PER_BLOCK 128 +/* flags used in the gpi address mask */ +#define GPI_ADDR_CITY 1 +#define GPI_ADDR_COUNTRY 2 +#define GPI_ADDR_STATE 4 +#define GPI_ADDR_POSTAL_CODE 8 +#define GPI_ADDR_ADDR 16 + static char *opt_cat, *opt_pos, *opt_notes, *opt_hide_bitmap, *opt_descr, *opt_bitmap; static arglist_t garmin_gpi_args[] = { @@ -93,6 +103,12 @@ typedef struct writer_data_s { struct writer_data_s *buttom_left; struct writer_data_s *buttom_right; } writer_data_t; + +typedef struct gpi_waypt_data_s { + int sz; + char *addr; + char *postal_code; +} gpi_waypt_data_t; typedef struct { gbint32 size; @@ -127,6 +143,17 @@ typedef struct { gbint32 size_2c; } gpi_bitmap_header_t; +typedef struct { + int sz; + short mask; + char addr_is_dynamic; + char *addr; + char *city; + char *country; + char *phone_nr; + char *postal_code; + char *state; +} gpi_waypt_t; static gbfile *fin, *fout; static gbint32 codepage; /* code-page, i.e. 1252 */ @@ -135,7 +162,8 @@ static writer_data_t *wdata; static short_handle short_h; #ifdef GPI_DBG -# define PP printf("@%1$6x (%1$8d): ", gbftell(fin)) +# define PP warning("@%1$6x (%1$8d): ", gbftell(fin)) +# define dbginfo warning #else # define PP #endif @@ -144,37 +172,70 @@ static short_handle short_h; * %%% gpi reader %%% * *******************************************************************************/ -/* read a string with embedded "EN" header */ -static char * -read_string(const int *sz) +/* look for or initialize GMSD */ +static garmin_fs_t * +gpi_gmsd_init(waypoint *wpt) { - char en[3]; - short slen; - char *res; - - gbfread(&en, 1, 2, fin); - - en[2] = '\0'; - if (strcmp(en, "EN") != 0) { - fatal(MYNAME ": Out of sync ('EN' expected)!\n"); + if (wpt == NULL) { + fatal(MYNAME ": Error in file structure.\n"); } - - slen = gbfgetint16(fin); - if (sz && (*sz != slen + 4)) { - fatal(MYNAME ": Out of sync (wrong string size)!\n"); + garmin_fs_t *gmsd = GMSD_FIND(wpt); + if (gmsd == NULL) { + gmsd = garmin_fs_alloc(-1); + fs_chain_add(&wpt->fs, (format_specific_data *) gmsd); } + return gmsd; +} + +/* read a standard string with or without 'EN' (or whatever) header */ +static char * +gpi_read_string(const char *field) +{ + int l1; + char *res = NULL; - if (slen > 0) { - res = xmalloc(slen + 1); - res[slen] = '\0'; - gbfread(res, 1, slen, fin); + l1 = gbfgetint16(fin); + if (l1 > 0) { + short l2; + char first; + + first = gbfgetc(fin); + if (first == 0) { + char en[2]; + + is_fatal((gbfgetc(fin) != 0), + MYNAME ": Error reading field '%s'!", field); + + gbfread(en, 1, sizeof(en), fin); + l2 = gbfgetint16(fin); + is_fatal((l2 + 4 != l1), + MYNAME ": Error out of sync (wrong size %d/%d) on field '%s'!", l1, l2, field); + + if ((en[0] < 'A') || (en[0] > 'Z') || (en[1] < 'A') || (en[1] > 'Z')) + fatal(MYNAME ": Invalid country code!\n"); + res = xmalloc(l2 + 1); + res[l2] = '\0'; + PP; + if (l2 > 0) + gbfread(res, 1, l2, fin); + } + else { + res = xmalloc(l1 + 1); + *res = first; + *(res + l1) = '\0'; + PP; + l1--; + if (l1 > 0) + gbfread(res + 1, 1, l1, fin); + + } } - else res = NULL; - +#ifdef GPI_DBG + dbginfo("%s: %s\n", field, (res == NULL) ? "" : res); +#endif return res; } - static void read_header(void) { @@ -199,7 +260,7 @@ read_header(void) tm.tm_year += 20; /* !!! */ tm.tm_mday -= 1; /* !!! */ strftime(stime, sizeof(stime), "%Y/%m/%d %H:%M:%S", &tm); - printf("crdate = %lu (%s)\n", rdata->crdate, stime); + dbginfo("crdate = %lu (%s)\n", rdata->crdate, stime); #endif (void) gbfgetint16(fin); /* 0 */ @@ -221,7 +282,7 @@ read_header(void) #ifdef GPI_DBG PP; - printf("< leaving header\n"); + dbginfo("< leaving header\n"); #endif } @@ -236,14 +297,14 @@ read_poi(const int sz) int pos, len; waypoint *wpt; -#if GPI_DBG +#ifdef GPI_DBG PP; - printf("> reading poi (size %d)\n", sz); + dbginfo("> reading poi (size %d)\n", sz); #endif PP; len = gbfgetint32(fin); /* sub-header size */ -#if GPI_DBG - printf("poi sublen = %1$d (0x%1$x)\n", len); +#ifdef GPI_DBG + dbginfo("poi sublen = %1$d (0x%1$x)\n", len); #endif pos = gbftell(fin); @@ -256,13 +317,7 @@ read_poi(const int sz) (void) gbfgetint16(fin); /* ? always 1 ? */ (void) gbfgetc(fin); /* seems to 1 when extra options present */ - len = gbfgetint32(fin); - - PP; - wpt->shortname = read_string(&len); -#ifdef GPI_DBG - printf("shortname = %s\n", wpt->shortname); -#endif + wpt->shortname = gpi_read_string("Shortname"); while (gbftell(fin) < (gbsize_t)(pos + sz - 4)) { int tag = gbfgetint32(fin); @@ -276,7 +331,7 @@ read_poi(const int sz) #ifdef GPI_DBG PP; - printf("< leaving poi\n"); + dbginfo("< leaving poi\n"); #endif } @@ -289,12 +344,12 @@ read_poi_list(const int sz) pos = gbftell(fin); #ifdef GPI_DBG PP; - printf("> reading poi list (-> %1$x / %1$d )\n", pos + sz); + dbginfo("> reading poi list (-> %1$x / %1$d )\n", pos + sz); #endif PP; i = gbfgetint32(fin); /* mostly 23 (0x17) */ #ifdef GPI_DBG - printf("list sublen = %1$d (0x%1$x)\n", i); + dbginfo("list sublen = %1$d (0x%1$x)\n", i); #endif (void) gbfgetint32(fin); /* max-lat */ (void) gbfgetint32(fin); /* max-lon */ @@ -313,7 +368,7 @@ read_poi_list(const int sz) } #ifdef GPI_DBG PP; - printf("< leaving poi list\n"); + dbginfo("< leaving poi list\n"); #endif } @@ -321,12 +376,12 @@ read_poi_list(const int sz) static void read_poi_group(const int sz, const int tag) { - int len, pos; + int pos; pos = gbftell(fin); #ifdef GPI_DBG PP; - printf("> reading poi group (-> %1$x / %1$d)\n", pos + sz); + dbginfo("> reading poi group (-> %1$x / %1$d)\n", pos + sz); #endif if (tag == 0x80009) { int subsz; @@ -334,17 +389,12 @@ read_poi_group(const int sz, const int tag) PP; subsz = gbfgetint32(fin); /* ? offset to category data ? */ #ifdef GPI_DBG - printf("group sublen = %d (-> %x / %d)\n", subsz, pos + subsz + 4, pos + subsz + 4); + dbginfo("group sublen = %d (-> %x / %d)\n", subsz, pos + subsz + 4, pos + subsz + 4); #endif } + if (rdata->group) xfree(rdata->group); /* currently unused */ + rdata->group = gpi_read_string("Group"); - len = gbfgetint32(fin); /* size of group string */ - PP; - if (rdata->group) xfree(rdata->group); - rdata->group = read_string(&len); -#ifdef GPI_DBG - printf("Group \"%s\"\n", rdata->group); -#endif while (gbftell(fin) < (gbsize_t)(pos + sz)) { int subtag = gbfgetint32(fin); if (! read_tag("read_poi_group", subtag, NULL)) break; @@ -352,7 +402,7 @@ read_poi_group(const int sz, const int tag) #ifdef GPI_DBG PP; - printf("< leaving poi group\n"); + dbginfo("< leaving poi group\n"); #endif } @@ -361,18 +411,17 @@ read_poi_group(const int sz, const int tag) static int read_tag(const char *caller, const int tag, waypoint *wpt) { - int pos, sz, len, flag; + int pos, sz; + short mask; char *str; -#ifdef GPI_DBG - int subtag; -#endif - + garmin_fs_t *gmsd; + sz = gbfgetint32(fin); pos = gbftell(fin); #ifdef GPI_DBG PP; - printf("%s: tag = 0x%x (size %d)\n", caller, tag, sz); + dbginfo("%s: tag = 0x%x (size %d)\n", caller, tag, sz); #endif if ((tag >= 0x80000) && (tag <= 0x800ff)) sz += 4; @@ -385,44 +434,26 @@ read_tag(const char *caller, const int tag, waypoint *wpt) case 0x5: /* group bitmap */ break; - case 0x7: /* category */ - (void) gbfgetint32(fin); - (void) gbfgetint16(fin); + case 0x7: + (void) gbfgetint16(fin); /* category number */ if (rdata->category) xfree(rdata->category); - PP; - rdata->category = read_string(NULL); -#ifdef GPI_DBG - printf("Category: \"%s\"\n", rdata->category); -#endif + rdata->category = gpi_read_string("Category"); break; - case 0xa: /* description */ - len = gbfgetint32(fin); - PP; - wpt->description = read_string(&len); -#ifdef GPI_DBG - printf("Description: \"%s\"\n", wpt->description); -#endif + case 0xa: + wpt->description = gpi_read_string("Description"); break; case 0xe: /* ? notes or description / or both ? */ - flag = gbfgetc(fin); - if (flag == 0x01) { - len = gbfgetint32(fin); - PP; - str = read_string(NULL); + mask = gbfgetc(fin); + if (mask == 0x01) { + str = gpi_read_string("Notes"); } - else if (flag == 0x32) { - len = gbfgetint16(fin); - str = xmalloc(len + 1); - str[len] = '\0'; - PP; - gbfread(str, 1, len, fin); + else if (mask == 0x32) { + str = gpi_read_string("Notes"); } else break; -#ifdef GPI_DBG - printf("Notes: \"%s\"\n", str); -#endif + if (wpt->description) wpt->notes = str; else wpt->description = str; break; @@ -441,25 +472,46 @@ read_tag(const char *caller, const int tag, waypoint *wpt) break; case 0x8000b: /* address (street/city...) */ - /* ToDo */ + (void) gbfgetint32(fin); + PP; + mask = gbfgetint16(fin); /* address fields mask */ +#ifdef GPI_DBG + dbginfo("GPI Address field mask: %d (0x%02x)\n", mask, mask); +#endif + if ((mask & GPI_ADDR_CITY) && (str = gpi_read_string("City"))) { + gmsd = gpi_gmsd_init(wpt); + GMSD_SET(city, str); + } + if ((mask & GPI_ADDR_COUNTRY) && (str = gpi_read_string("Country"))) { + gmsd = gpi_gmsd_init(wpt); + GMSD_SET(country, str); + } + if ((mask & GPI_ADDR_STATE) && (str = gpi_read_string("State"))) { + gmsd = gpi_gmsd_init(wpt); + GMSD_SET(state, str); + } + if ((mask & GPI_ADDR_POSTAL_CODE) && (str = gpi_read_string("Postal code"))) { + gmsd = gpi_gmsd_init(wpt); + GMSD_SET(postal_code, str); + } + if ((mask & GPI_ADDR_ADDR) && (str = gpi_read_string("Street address"))) { + gmsd = gpi_gmsd_init(wpt); + GMSD_SET(addr, str); + } break; case 0x8000c: /* phone-number */ -#ifdef GPI_DBG - PP; - subtag = gbfgetint32(fin); - printf("phone-number tag %d\n", subtag); - (void) gbfgetint16(fin); /* unknown / ? phone/fax/mobil ? */ - PP; - len = gbfgetint16(fin); - printf("phone-number len %d\n", len); - str = xmalloc(len + 1); - str[len] = '\0'; + (void) gbfgetint32(fin); PP; - gbfread(str, 1, len, fin); - printf("phone-number \"%s\"\n", str); - xfree(str); + + mask = gbfgetint16(fin); /* phone fields mask */ +#ifdef GPI_DBG + dbginfo("GPI Phone field mask: %d (0x%02x)\n", mask, mask); #endif + if ((mask & 1) && (str = gpi_read_string("Phone"))) { + gmsd = gpi_gmsd_init(wpt); + GMSD_SET(phone_nr, str); + } break; case 0x80012: /* ? sounds / images ? */ @@ -478,14 +530,15 @@ read_tag(const char *caller, const int tag, waypoint *wpt) *******************************************************************************/ static void -write_string(const char *str) +write_string(const char *str, const char long_format) { int len; len = strlen(str); - - gbfputint32(len + 4, fout); - gbfwrite("EN", 1, 2, fout); + if (long_format) { + gbfputint32(len + 4, fout); + gbfwrite("EN", 1, 2, fout); + } gbfputint16(len, fout); gbfwrite(str, 1, len, fout); } @@ -534,7 +587,11 @@ wdata_free(writer_data_t *data) QUEUE_FOR_EACH(&data->Q, elem, tmp) { waypoint *wpt = (waypoint *)elem; - if (wpt->extra_data) xfree(wpt->extra_data); + if (wpt->extra_data) { + gpi_waypt_t *dt = (gpi_waypt_t *) wpt->extra_data; + if (dt->addr_is_dynamic) xfree(dt->addr); + xfree(dt); + } waypt_free(wpt); } @@ -623,6 +680,8 @@ wdata_compute_size(writer_data_t *data) QUEUE_FOR_EACH(&data->Q, elem, tmp) { waypoint *wpt = (waypoint *) elem; + gpi_waypt_t *dt; + garmin_fs_t *gmsd; char *str; res += 12; /* tag/sz/sub-sz */ @@ -642,14 +701,47 @@ wdata_compute_size(writer_data_t *data) else if (opt_pos) str = pretty_deg_format(wpt->latitude, wpt->longitude, 's', " ", 0); - if (str) { /* this will be stored into street-address field */ - res += 22 + strlen(str); - wpt->extra_data = str; + dt = xcalloc(1, sizeof(*dt)); + wpt->extra_data = dt; + + if (str) { + dt->addr_is_dynamic = 1; + dt->addr = str; + dt->mask |= GPI_ADDR_ADDR; + dt->sz += (8 + strlen(dt->addr)); + } + + if ((gmsd = GMSD_FIND(wpt))) { + if ((dt->mask == 0) && ((dt->addr = GMSD_GET(addr, NULL)))) { + dt->mask |= GPI_ADDR_ADDR; + dt->sz += (8 + strlen(dt->addr)); + } + if ((dt->city = GMSD_GET(city, NULL))) { + dt->mask |= GPI_ADDR_CITY; + dt->sz += (8 + strlen(dt->city)); + } + if ((dt->country = GMSD_GET(country, NULL))) { + dt->mask |= GPI_ADDR_COUNTRY; + dt->sz += (8 + strlen(dt->country)); + } + if ((dt->state = GMSD_GET(state, NULL))) { + dt->mask |= GPI_ADDR_STATE; + dt->sz += (8 + strlen(dt->state)); + } + if ((dt->postal_code = GMSD_GET(postal_code, NULL))) { + dt->mask |= GPI_ADDR_POSTAL_CODE; + dt->sz += (2 + strlen(dt->postal_code)); /* short form */ + } + + if ((dt->phone_nr = GMSD_GET(phone_nr, NULL))) + res += (12 + 4 + strlen(dt->phone_nr)); } + if (dt->mask) dt->sz += 2; /* + mask (two bytes) */ + if (dt->sz) res += (dt->sz + 12); /* + header size */ str = wpt->description; if (! str) str = wpt->notes; - if (str) res += (16 + strlen(str)); + if (str) res += (12 + 4 + strlen(str)); } if (data->top_left) res += wdata_compute_size(data->top_left); @@ -659,7 +751,7 @@ wdata_compute_size(writer_data_t *data) data->sz = res; - return res + 12; /* 12 = caller needs info about tag header size */ + return res + 12; /* + 12 = caller needs info about tag header size */ } @@ -687,6 +779,7 @@ wdata_write(const writer_data_t *data) char *str; int s0, s1; waypoint *wpt = (waypoint *)elem; + gpi_waypt_t *dt = wpt->extra_data; str = wpt->description; if (! str) str = wpt->notes; @@ -695,10 +788,9 @@ wdata_write(const writer_data_t *data) s0 = s1 = 19 + strlen(wpt->shortname); s0 += 10; /* tag(4) */ - if (str) s0 += (16 + strlen(str)); /* descr */ - - if (wpt->extra_data) - s0 += 22 + strlen((char *)wpt->extra_data); + if (str) s0 += (12 + 4 + strlen(str)); /* descr */ + if (dt->sz) s0 += (12 + dt->sz); /* address part */ + if (dt->phone_nr) s0 += (12 + 4 + strlen(dt->phone_nr)); gbfputint32(s0, fout); /* size of following data (tag) */ gbfputint32(s1, fout); /* basic size (without options) */ @@ -709,26 +801,37 @@ wdata_write(const writer_data_t *data) gbfputint16(1, fout); /* ? always 1 ? */ gbfputc(0, fout); /* seems to be 1 when extra options present */ - write_string(wpt->shortname); + write_string(wpt->shortname, 1); gbfputint32(4, fout); /* tag(4) */ - gbfputint32(2, fout); + gbfputint32(2, fout); /* ? always 2 == version ??? */ if (opt_hide_bitmap) gbfputint16(0x3ff, fout); /* values != 0 hides the bitmap */ else gbfputint16(0, fout); if (str) { gbfputint32(0xa, fout); gbfputint32(strlen(str) + 8, fout); /* string + string header */ - write_string(str); + write_string(str, 1); } - str = (char *)wpt->extra_data; - if (str) { + if (dt->sz) { /* gpi address */ gbfputint32(0x8000b, fout); - gbfputint32(strlen(str) + 10, fout); - gbfputint32(0x2, fout); /* ? always 2 ? */ - gbfputint16(0x10, fout); /* 0x10 = StreetAddress */ - write_string(str); + gbfputint32(dt->sz, fout); + gbfputint32(0x2, fout); /* ? always 2 ? */ + gbfputint16(dt->mask, fout); + if (dt->mask & GPI_ADDR_CITY) write_string(dt->city, 1); + if (dt->mask & GPI_ADDR_COUNTRY) write_string(dt->country, 1); + if (dt->mask & GPI_ADDR_STATE) write_string(dt->state, 1); + if (dt->mask & GPI_ADDR_POSTAL_CODE) write_string(dt->postal_code, 0); + if (dt->mask & GPI_ADDR_ADDR) write_string(dt->addr, 1); + } + + if (dt->phone_nr) { + gbfputint32(0x8000c, fout); + gbfputint32(strlen(dt->phone_nr) + 2 + 2, fout); + gbfputint32(0x2, fout); /* ? always 2 ? */ + gbfputint16(1, fout); /* mask */ + write_string(dt->phone_nr, 0); } } @@ -754,7 +857,7 @@ write_category(const char *category, const char *image, const int image_sz) else gbfputint32(sz, fout); gbfputint32(sz, fout); - write_string(opt_cat); + write_string(opt_cat, 1); wdata_write(wdata); @@ -902,7 +1005,7 @@ load_bitmap_from_file(const char *fname, char **data, int *data_sz) dest_line_sz = ((int)((dest_line_sz + 3) / 4)) * 4; sz = sizeof(*dest_h) + (src_h.height * dest_line_sz); - if (src_h.used_colors) sz += src_h.used_colors * 4; + if (src_h.used_colors) sz += (src_h.used_colors * 4); ptr = xmalloc(sz); dest_h = (void *)ptr; @@ -924,7 +1027,7 @@ load_bitmap_from_file(const char *fname, char **data, int *data_sz) /* copy and revert order of BMP lines */ ptr = (void *)dest_h; - ptr += sizeof(*dest_h) + (dest_line_sz * (src_h.height - 1)); + ptr += (sizeof(*dest_h) + (dest_line_sz * (src_h.height - 1))); if (src_h.bpp == 24) { /* 24 bpp seems to be not supported, convert to 32 bpp */ @@ -948,7 +1051,7 @@ load_bitmap_from_file(const char *fname, char **data, int *data_sz) if (src_h.used_colors > 0) { ptr = (void *)dest_h; - ptr += sizeof(*dest_h) + (src_h.height * src_line_sz); + ptr += (sizeof(*dest_h) + (src_h.height * src_line_sz)); for (i = 0; i < src_h.used_colors; i++) { le_write32(ptr, color_table[i]); @@ -1066,7 +1169,7 @@ garmin_gpi_write(void) else if (opt_bitmap && *opt_bitmap) load_bitmap_from_file(opt_bitmap, &image, &image_sz); else { - image = gpi_bitmap; /* embedded image in gpi format */ + image = gpi_bitmap; /* embedded GPSBabel icon in gpi format */ image_sz = GPI_BITMAP_SIZE; } waypt_disp_all(enum_waypt_cb); -- 2.30.2